/*
    This file is part of Posixcpp library.

    Posixcpp library is free software: you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public License
    as published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Posixcpp library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU LesserGeneral Public
    License along with Posixcpp library. If not, see
    <http://www.gnu.org/licenses/>.
*/

#include "posix_threads_condition.hpp"

using posix::threads::Condition;

Condition::Condition( ) :
m_lock(NULL)
{
	POSIX_CALL_ASSERT_X( (errno = pthread_cond_init(&m_handle, NULL)) == 0,
            "Unable to initialize condition variable" );
}

Condition::Condition( Mutex & mutexObj ) :
m_lock(&mutexObj)
{
    POSIX_CALL_ASSERT_X( (errno = pthread_cond_init(&m_handle, NULL)) == 0,
            "Unable to initialize condition variable" );
}

Condition::Condition( const Attributes& attrs ) :
m_lock(NULL)
{
	POSIX_CALL_ASSERT_X( (errno = pthread_cond_init( &m_handle,
                    &attrs.handle()) ) == 0,
            "Unable to initialize condition variable" );
}

Condition::Condition( const Attributes & attrs, Mutex &mutexObj ) :
m_lock(&mutexObj)
{
    POSIX_CALL_ASSERT_X( (errno = pthread_cond_init( &m_handle,
                    &attrs.handle()) ) == 0,
            "Unable to initialize condition variable" );
}

Condition::~Condition()
{
    POSIX_CALL_ASSERT_X((errno = pthread_cond_destroy(&m_handle)) == 0,
					"Unable to destroy condition variable");
}

void Condition::signal()
{
	POSIX_CALL_ASSERT_X((errno = pthread_cond_signal(&m_handle)) == 0,
					"Unable to signal on condition variable");
}

void Condition::broadcast()
{
	POSIX_CALL_ASSERT_X((errno = pthread_cond_broadcast(&m_handle)) == 0,
					"Unable to broadcast on condition variable");
}

void Condition::wait( Mutex * mutexObj )
{
    PRECONDITION_ASSERT( (mutexObj != NULL) || (m_lock != NULL),
        "If the default mutex was not set by the constructor, \
            one should be assigned by the wait call parameter" );

    if (mutexObj)
    {
        POSIX_CALL_ASSERT_X( (errno = pthread_cond_wait(&m_handle,
                        &mutexObj->handle())) == 0,
                "Unable to lock on condition variable" );
    }
    else if (m_lock)
    {
        POSIX_CALL_ASSERT_X((errno = pthread_cond_wait(&m_handle,
                        &m_lock->handle())) == 0,
                "Unable to lock on condition variable");
    }
    else
        throw std::runtime_error("Mutex object not defined");
}

bool Condition::wait( time::Timespec timeout,  Mutex * mutexObj )
{
    PRECONDITION_ASSERT( (mutexObj != NULL) || (m_lock != NULL),
        "If the default mutex was not set by the constructor, \
            one should be assigned by the wait call" );

    int res;

    if (mutexObj)
        res = pthread_cond_timedwait(&m_handle,
									&(mutexObj->handle()),
									&timeout.data());
    else if (m_lock)
        res = pthread_cond_timedwait(&m_handle,
									&(m_lock->handle()),
									&timeout.data());
    else
        throw std::runtime_error("Mutex object not defined");

	switch (res)
	{
	case 0:
		return true;
	case ETIMEDOUT:
		return false;
	default:
		POSIX_CALL_ASSERT_X(res == 0,
						"Unable to process timed wait on condition");
		return false;
	}
}
